package com.hitqz.robot.biz.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.cache.Cache;
import com.hitqz.robot.api.business.dto.CmdRobot2PosDto;
import com.hitqz.robot.api.business.dto.RobotClientSummaryDto;
import com.hitqz.robot.api.business.dto.RobotCustomInfoDto;
import com.hitqz.robot.api.business.dto.RobotLaserPosDto;
import com.hitqz.robot.api.business.dto.openapi.CvdGuideTaskDTO;
import com.hitqz.robot.api.business.dto.openapi.CvdGuideTaskStatusDTO;
import com.hitqz.robot.api.business.dto.openapi.CvdQuestionTextDTO;
import com.hitqz.robot.api.business.entity.*;
import com.hitqz.robot.api.business.enums.GuideTaskStatusEnum;
import com.hitqz.robot.api.business.enums.GuideTaskStepActionEnum;
import com.hitqz.robot.api.business.enums.GuideTaskStepStatusEnum;
import com.hitqz.robot.api.dispatch.protocol.dto.RobotCmdNavDto;
import com.hitqz.robot.biz.service.*;
import com.hitqz.robot.biz.websocket.endpoint.SubscriptionEndpoint;
import com.hitqz.robot.common.core.util.R;
import com.hitqz.robot.common.file.core.FileProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.sql.Time;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author kehanjiang
 * @date 2025/09/30
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ApiServiceImpl implements ApiService {
    private final CvdGuidePosService cvdGuidePosService;
    private final CvdGuideTaskService cvdGuideTaskService;
    private final CvdGuideTaskStepService cvdGuideTaskStepService;
    private final RobotMapPosService robotMapPosService;
    private final RobotService robotService;
    private final CvdVoiceService cvdVoiceService;
    private final FileProperties fileProperties;
    private final CvdTalkHistoryService cvdTalkHistoryService;
    private final CvdQuestionAnswerService cvdQuestionAnswerService;

    private final RestTemplate restTemplate;

    @Value("${convert.voice-to-text}")
    private String voiceToTextUrl;

    private final static String TOPIC_GUIDE = "guide";
    private final static String TOPIC_QUESTION = "question";

    private final static String AUDIO_PLAY_RECORD = "http://127.0.0.1:8181/audio/record";
    private final static String AUDIO_PLAY_URL = "http://127.0.0.1:8181/audio/play";
    private final static String AUDIO_PROCESS_URL = "http://127.0.0.1:8181/audio/process";

    private final static ReentrantLock talkLock = new ReentrantLock();

    @Value("${server.real-ip:127.0.0.1}")
    private String serverRealIp;
    @Value("${server.port}")
    private Integer serverPort;
    @Value("${server.servlet.context-path:}")
    private String serverServletContextPath;

    @Override
    public Boolean guideModify(CvdGuideTaskDTO dto) {
        String endAction = dto.getEndAction();
        List<Long> posList = dto.getPosList();
        Assert.notEmpty(posList, "位置ID集合不可为空！");
        Assert.notNull(endAction, "任务结束后执行动作不可为空！");
        return cvdGuideTaskService.update(Wrappers.<CvdGuideTaskEntity>lambdaUpdate()
                .set(CvdGuideTaskEntity::getPos, StringUtils.join(posList, ","))
                .set(CvdGuideTaskEntity::getEndAction, endAction));
    }

    @Override
    public CvdGuideTaskDTO guideDetail() {
        CvdGuideTaskEntity entity = cvdGuideTaskService.getOne(Wrappers
                .<CvdGuideTaskEntity>lambdaQuery()
                .last("limit 1"));
        CvdGuideTaskDTO dto = new CvdGuideTaskDTO();
        String pos = entity.getPos();
        List<Long> posList = Arrays.stream(pos.split(",")).map(Long::parseLong).toList();
        dto.setPosList(posList);
        dto.setEndAction(entity.getEndAction());
        return dto;
    }

    @Override
    public List<CvdGuidePosEntity> guidePosList() {
        return cvdGuidePosService.list();
    }

    /**
     * 迎宾讲解操作
     *
     * @param cmd 操作：0-开始任务 1-暂停任务 2-继续任务 3-终止任务 4-跳过讲解 5-重复讲解
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean guideOperate(String cmd) {
        RobotEntity robot = robotService.getOne(Wrappers.<RobotEntity>lambdaQuery().last("limit 1"));
        if (robot == null) {
            throw new RuntimeException("未找到机器人！");
        }
        CvdGuideTaskEntity task = cvdGuideTaskService.getOne(Wrappers
                .<CvdGuideTaskEntity>lambdaQuery()
                .last("limit 1"));
        GuideTaskStatusEnum taskStatus = GuideTaskStatusEnum.getByCode(task.getStatus());
        if (task == null) {
            throw new RuntimeException("未找到迎宾讲解任务！");
        }
        switch (cmd) {
            case "0": { //开始任务
                //修改迎宾讲解任务 状态为执行中
                Long execNo = System.currentTimeMillis();
                task.setExecNo(execNo);
                task.setStatus(GuideTaskStatusEnum.RUNNING.getCode());//执行中
                cvdGuideTaskService.updateById(task);
                //生成迎宾讲解任务步骤
                String pos = task.getPos();
                List<Long> posList = Arrays.stream(pos.split(",")).map(Long::parseLong).toList();
                for (Long posId : posList) {

                    CvdGuidePosEntity posEntity = cvdGuidePosService.getById(posId);
                    String code = posEntity.getCode();
                    Long voiceId = posEntity.getVoiceId();
                    //前往点位步骤
                    CvdGuideTaskStepEntity goStep = new CvdGuideTaskStepEntity();
                    goStep.setStatus(GuideTaskStepStatusEnum.INIT.getCode());
                    goStep.setExecNo(execNo);
                    goStep.setGuidePosId(posId);
                    goStep.setAction(GuideTaskStepActionEnum.GO.getCode());
                    goStep.setParam(JSONUtil.toJsonStr(
                            Map.of("code", code)
                    ));
                    cvdGuideTaskStepService.save(goStep);
                    //语音介绍步骤
                    CvdGuideTaskStepEntity speakStep = new CvdGuideTaskStepEntity();
                    speakStep.setStatus(GuideTaskStepStatusEnum.INIT.getCode());
                    speakStep.setExecNo(execNo);
                    speakStep.setGuidePosId(posId);
                    speakStep.setAction(GuideTaskStepActionEnum.SPEAK.getCode());
                    speakStep.setParam(JSONUtil.toJsonStr(
                            Map.of("voiceId", String.valueOf(voiceId))
                    ));
                    cvdGuideTaskStepService.save(speakStep);
                }
                //任务结束后执行动作
                String endAction = task.getEndAction();
                if ("1".equals(endAction)) {//返回充电
                    CvdGuideTaskStepEntity endStep = new CvdGuideTaskStepEntity();
                    endStep.setStatus(GuideTaskStepStatusEnum.INIT.getCode());
                    endStep.setExecNo(execNo);
                    endStep.setAction(GuideTaskStepActionEnum.END.getCode());
                    RobotMapPosEntity chargePos = robotMapPosService.getOne(Wrappers.<RobotMapPosEntity>lambdaQuery()
                            .like(RobotMapPosEntity::getMapName, "充电")
                            .last("limit 1")
                    );
                    endStep.setParam(JSONUtil.toJsonStr(
                            Map.of("code", chargePos != null ? chargePos.getCode() : "1")
                    ));
                    cvdGuideTaskStepService.save(endStep);
                }
                break;
            }
            case "1": {
                //暂停任务
                task.setStatus(GuideTaskStatusEnum.PAUSE.getCode());
                cvdGuideTaskService.updateById(task);
                CvdGuideTaskStepEntity doingStep = cvdGuideTaskStepService.getOne(Wrappers.<CvdGuideTaskStepEntity>lambdaQuery()
                        .eq(CvdGuideTaskStepEntity::getExecNo, task.getExecNo())
                        .eq(CvdGuideTaskStepEntity::getStatus, GuideTaskStepStatusEnum.DOING.getCode())
                        .last("limit 1")
                        .orderByDesc(CvdGuideTaskStepEntity::getId)
                );
                if (doingStep != null) {
                    String action = doingStep.getAction();
                    //状态改成初始化
                    doingStep.setStatus(GuideTaskStepStatusEnum.INIT.getCode());
                    cvdGuideTaskStepService.updateById(doingStep);
                    if (GuideTaskStepActionEnum.SPEAK.getCode().equals(action)) {
                        //终止语音讲解
                        this.voiceOperate(1, "");
                    } else if (GuideTaskStepActionEnum.GO.getCode().equals(action)
                            || GuideTaskStepActionEnum.END.getCode().equals(action)) {
                        //暂停导航
                        SpringUtil.getApplicationContext().publishEvent(new RobotCmdNavDto(robot.getRobotSn(), 1));
                    }
                }
                break;
            }
            case "2": {
                //继续任务
                task.setStatus(GuideTaskStatusEnum.RUNNING.getCode());
                cvdGuideTaskService.updateById(task);
                break;
            }
            case "3": {
                //终止任务
                CvdGuideTaskStepEntity doingStep = cvdGuideTaskStepService.getOne(Wrappers.<CvdGuideTaskStepEntity>lambdaQuery()
                        .eq(CvdGuideTaskStepEntity::getExecNo, task.getExecNo())
                        .eq(CvdGuideTaskStepEntity::getStatus, GuideTaskStepStatusEnum.DOING.getCode())
                        .last("limit 1")
                        .orderByDesc(CvdGuideTaskStepEntity::getId)
                );
                if (doingStep != null) {
                    String action = doingStep.getAction();
                    if (GuideTaskStepActionEnum.SPEAK.getCode().equals(action)) {
                        //终止语音讲解
                        this.voiceOperate(1, "");
                    } else if (GuideTaskStepActionEnum.GO.getCode().equals(action)
                            || GuideTaskStepActionEnum.END.getCode().equals(action)) {
                        //停止导航
                        SpringUtil.getApplicationContext().publishEvent(new RobotCmdNavDto(robot.getRobotSn(), 3));
                    }
                }
                task.setStatus(GuideTaskStatusEnum.FREE.getCode());
                cvdGuideTaskService.updateById(task);
                cvdGuideTaskStepService.update(Wrappers.<CvdGuideTaskStepEntity>lambdaUpdate()
                        .set(CvdGuideTaskStepEntity::getStatus, GuideTaskStepStatusEnum.DONE.getCode())
                        .eq(CvdGuideTaskStepEntity::getExecNo, task.getExecNo()));
                break;
            }
            case "4": {
                //跳过讲解
                Long execNo = task.getExecNo();
                //终止语音讲解
                this.voiceOperate(1, "");
                CvdGuideTaskStepEntity speakStep = cvdGuideTaskStepService.getOne(Wrappers.<CvdGuideTaskStepEntity>lambdaQuery()
                        .eq(CvdGuideTaskStepEntity::getExecNo, execNo)
                        .eq(CvdGuideTaskStepEntity::getAction, GuideTaskStepActionEnum.SPEAK.getCode())
                        .eq(CvdGuideTaskStepEntity::getStatus, GuideTaskStepStatusEnum.DOING.getCode())
                        .last("limit 1")
                        .orderByDesc(CvdGuideTaskStepEntity::getId));
                if (speakStep != null) {
                    //状态改成完成
                    speakStep.setStatus(GuideTaskStepStatusEnum.DONE.getCode());
                    cvdGuideTaskStepService.updateById(speakStep);
                }
                break;
            }
            case "5": {
                //重复讲解 (获取最近一条讲解任务进行重复讲解)
                if (taskStatus == GuideTaskStatusEnum.FREE) return true;
                Long execNo = task.getExecNo();
                CvdGuideTaskStepEntity lastDoneGoStep = cvdGuideTaskStepService.getOne(Wrappers.<CvdGuideTaskStepEntity>lambdaQuery()
                        .eq(CvdGuideTaskStepEntity::getExecNo, execNo)
                        .eq(CvdGuideTaskStepEntity::getAction, GuideTaskStepActionEnum.GO.getCode())
                        .eq(CvdGuideTaskStepEntity::getStatus, GuideTaskStepStatusEnum.DONE.getCode())
                        .last("limit 1")
                        .orderByDesc(CvdGuideTaskStepEntity::getId)
                );
                CvdVoiceEntity voice = null;
                if (lastDoneGoStep != null) {
                    Long donePosId = lastDoneGoStep.getGuidePosId();
                    CvdGuidePosEntity donePos = cvdGuidePosService.getOne(Wrappers.<CvdGuidePosEntity>lambdaQuery()
                            .eq(CvdGuidePosEntity::getId, donePosId)
                            .last("limit 1")
                    );
                    if (donePos != null) {
                        voice = cvdVoiceService.getOne(Wrappers.<CvdVoiceEntity>lambdaQuery()
                                .eq(CvdVoiceEntity::getId, donePos.getVoiceId())
                                .last("limit 1")
                        );
                    }
                }
                if (taskStatus == GuideTaskStatusEnum.PAUSE) {
                    //取当前点位对应的语音
                    if (voice != null) {
                        //终止语音
                        this.voiceOperate(1, "");
                        //播放
                        String mediaUrl = voice.getMediaUrl();
                        String mediaName = mediaUrl.replaceAll("/sys-file/guide_voice/", "");
                        String basePath = fileProperties.getLocal().getBasePath();
                        this.voiceOperate(0, basePath + "guide_voice" + File.separator + mediaName);
                    }
                } else if (taskStatus == GuideTaskStatusEnum.RUNNING) {
                    CvdGuideTaskStepEntity lastDoingStep = cvdGuideTaskStepService.getOne(Wrappers.<CvdGuideTaskStepEntity>lambdaQuery()
                            .eq(CvdGuideTaskStepEntity::getExecNo, execNo)
                            .eq(CvdGuideTaskStepEntity::getStatus, GuideTaskStepStatusEnum.DOING.getCode())
                            .last("limit 1")
                            .orderByAsc(CvdGuideTaskStepEntity::getId)
                    );
                    if (lastDoingStep != null) {
                        if (GuideTaskStepActionEnum.SPEAK.getCode().equals(lastDoingStep.getAction())) {
                            //设置为初始化
                            lastDoingStep.setStatus(GuideTaskStepStatusEnum.INIT.getCode());
                            cvdGuideTaskStepService.updateById(lastDoingStep);
                            //终止语音
                            this.voiceOperate(1, "");
                        } else {
                            //取当前点位对应的语音
                            if (voice != null) {
                                //终止语音
                                this.voiceOperate(1, "");
                                //播放
                                String mediaUrl = voice.getMediaUrl();
                                String mediaName = mediaUrl.replaceAll("/sys-file/guide_voice/", "");
                                String basePath = fileProperties.getLocal().getBasePath();
                                this.voiceOperate(0, basePath + "guide_voice" + File.separator + mediaName);
                            }
                        }
                    }
                }
                break;
            }
        }
        return true;
    }


    /**
     * 语音操作接口
     *
     * @param operate 操作：0-开始播放、1-停止播放、2-暂停播放、3-恢复播放
     * @param wavPath 音频文件路径
     */
    private void voiceOperate(int operate, String wavPath) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.valueOf("application/json;UTF-8"));
        String body = JSONUtil.toJsonStr(Map.of("operate", operate, "wavPath", wavPath));
        HttpEntity<String> strEntity = new HttpEntity<String>(body, headers);
        log.info("语音操作接口，入参：{}", body);
        String result = restTemplate.postForObject(AUDIO_PLAY_URL, strEntity, String.class);
        log.info("语音操作接口，结果：{}", result);
    }

    /**
     * 语音是否播放结束
     */
    private Boolean voiceIsFinished() {
        try {
            ResponseEntity<String> rawResponse = restTemplate.exchange(AUDIO_PROCESS_URL, HttpMethod.GET,
                    new HttpEntity<>(new HttpHeaders()), String.class);
            String jsonStr = rawResponse.getBody();
//            log.info("语音是否播放结束接口，结果：{}", jsonStr);
            JSONObject jsonObject = JSONObject.parseObject(jsonStr);
            int code = jsonObject.getInteger("code");
            if (code != 0) {
                return false;
            } else {
                JSONObject data = jsonObject.getJSONObject("data");
                int playStatus = data.getInteger("playStatus");
                int progress = data.getInteger("progress");
                return playStatus == 0 || progress == 100;
            }
        } catch (Exception e) {
            log.error("语音是否播放结束接口异常:{}", e.getMessage(), e);
        }
        return false;
    }


    private static String currentWavPath = "";

    /**
     * 语音交互操作---音频输入
     *
     * @param cmd 操作：0-唤醒 1-关闭
     * @return
     */
    @Override
    public Boolean questionVoiceOperate(String cmd) {
        try {
            talkLock.lock();
            switch (cmd) {
                case "0":
                    //唤醒
                    currentWavPath = this.recordVoice(0);
                    break;
                case "1":
                    if (StringUtils.isNotEmpty(currentWavPath)) {
                        //关闭
                        this.recordVoice(1);
                        final String finalCurrentWavPath = this.normalizePath(currentWavPath);
                        ThreadUtil.execAsync(() -> {
                            try {
                                String dirPath = fileProperties.getLocal().getBasePath() + "question_voice" + File.separator;
                                dirPath = this.normalizePath(dirPath);
                                File tagertFile = this.moveFileToDirectory(finalCurrentWavPath, dirPath);
                                final String wavPath = "/sys-file/question_voice/" + tagertFile.getName();
                                //语音转文字
                                String content = this.voiceToText(wavPath);
                                //生成问题
                                CvdTalkHistoryEntity talkHistory = new CvdTalkHistoryEntity();
                                talkHistory.setBelong("user");
                                talkHistory.setContent(content);
                                talkHistory.setMediaUrl(wavPath);
                                talkHistory.setIsPlay("1");//已播放 （来自客户不需要再播放）
                                talkHistory.setIsConvert("1");//已转换
                                cvdTalkHistoryService.save(talkHistory);
                                //生成回答
                                this.generateAnswer(talkHistory.getContent());
                            } catch (Exception e) {
                                log.error("执行语音交互操作异常:{}", e.getMessage(), e);
                            }
                        });
                    }
                    currentWavPath = "";
                    break;
            }
        } finally {
            talkLock.unlock();
        }
        return true;
    }

    /**
     * 转移文件到指定文件夹内
     *
     * @param sourcePath      源文件路径
     * @param targetDirectory 目标目录路径
     * @return 返回文件新路径
     */
    public static File moveFileToDirectory(String sourcePath, String targetDirectory) {
        log.info("moveFileToDirectory sourcePath===>{}", sourcePath);
        log.info("moveFileToDirectory targetDirectory===>{}", targetDirectory);
        // 检查源文件是否存在
        AtomicInteger count = new AtomicInteger(0);
        while (!FileUtil.exist(sourcePath)) {
            if (count.getAndIncrement() > 10) {
                throw new RuntimeException("源文件不存在: " + sourcePath);
            }
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        }

        // 创建目标目录（如果不存在）
        FileUtil.mkdir(targetDirectory);

        // 构建目标文件路径
        String fileName = FileUtil.getName(sourcePath);
        String targetFilePath = FileUtil.normalize(targetDirectory + File.separator + fileName);
        File sourceFile = new File(sourcePath);
        File targetFile = new File(targetFilePath);
        // 执行文件转移
        FileUtil.move(sourceFile, targetFile, true);
        return targetFile;
    }

    /**
     * 语音交互操作---文字输入
     *
     * @param dto
     * @return
     */
    @Override
    public Boolean questionTextOperate(CvdQuestionTextDTO dto) {
        try {
            talkLock.lock();
            //生成问题
            CvdTalkHistoryEntity talkHistory = new CvdTalkHistoryEntity();
            talkHistory.setBelong("user");
            talkHistory.setContent(dto.getQuestionText());
            talkHistory.setIsPlay("1");//已播放
            talkHistory.setIsConvert("1");//已转换
            cvdTalkHistoryService.save(talkHistory);
            ThreadUtil.execAsync(() -> {
                try {
                    //生成回答
                    this.generateAnswer(talkHistory.getContent());
                } catch (Exception e) {
                    log.error("执行语音交互操作异常:{}", e.getMessage(), e);
                }
            });
        } finally {
            talkLock.unlock();
        }
        return true;
    }

    /**
     * 生成回答
     *
     * @param questionContent
     */
    private void generateAnswer(String questionContent) throws Exception {
        List<CvdQuestionAnswerEntity> answerList = cvdQuestionAnswerService.list();
        Map<Long, CvdQuestionAnswerEntity> ansewerMap = answerList.stream().collect(Collectors.toMap(CvdQuestionAnswerEntity::getId, v -> v));
        Map<Long, Integer> hitMap = new HashMap<>();
        for (CvdQuestionAnswerEntity answer : answerList) {//计算每个词条 关键词 命中次数
            String[] labels = answer.getLabel().split(",");
            for (String label : labels) {
                if (questionContent.contains(label)) {
                    hitMap.put(answer.getId(), hitMap.getOrDefault(answer.getId(), 0) + 1);
                }
            }
        }
        CvdQuestionAnswerEntity answer = cvdQuestionAnswerService.getOne(Wrappers.<CvdQuestionAnswerEntity>lambdaQuery()
                .eq(CvdQuestionAnswerEntity::getType, "系统内置")
                .eq(CvdQuestionAnswerEntity::getLabel, "无法识别")
                .last("limit 1")
        );
        if (answer == null) {
            String errorMsg = "没有匹配到词条时，未设置默认词条！";
            log.error(errorMsg);
            throw new RuntimeException(errorMsg);
        }
        if (hitMap.size() > 0) {
            Long maxHit = hitMap.entrySet().stream().max(Map.Entry.comparingByValue()).get().getKey();
            answer = ansewerMap.get(maxHit);
            Cache<String, RobotClientSummaryDto> posInfoDtoCache = SpringUtil.getBean("robotCache");
            Iterable<String> cacheKeys = posInfoDtoCache.asMap().keySet();
            String firstKey = cacheKeys.iterator().hasNext() ? cacheKeys.iterator().next() : null;
            RobotClientSummaryDto robotClientSummaryDto = firstKey != null ? posInfoDtoCache.getIfPresent(firstKey) : null;
            if ("机器人状态".equals(answer.getLabel())) {
                if (robotClientSummaryDto == null || !"在线".equals(
                        robotClientSummaryDto.getStatus().getOnlineStatusStr()
                )) {
                    answer.setAnswerContent("机器人未上线，请先检查机器人状态！");
                } else {
                    String status = "电量：%s，电压：%s，电流：%s，急停状态：%s";
                    answer.setAnswerContent("当前机器人状态如下：" + status.formatted(
                            robotClientSummaryDto.getStatus().getBattery() + "%",
                            robotClientSummaryDto.getStatus().getVoltage() + "V",
                            robotClientSummaryDto.getStatus().getCurrent() + "A",
                            robotClientSummaryDto.getStatus().getEmergencyStopStr()
                    ));
                }
                answer.setMediaStatus("0");
                cvdQuestionAnswerService.updateById(answer);
            } else if ("空气质量".equals(answer.getLabel())) {
                if (robotClientSummaryDto == null || !"在线".equals(
                        robotClientSummaryDto.getStatus().getOnlineStatusStr()
                )) {
                    answer.setAnswerContent("机器人未上线，请先检查机器人状态！");
                } else {
                    String status = "三甲基硅烷：%s，氢气：%s，丙烯：%s，温度：%s，湿度：%s";
                    List<RobotCustomInfoDto> homeShowInfo = robotClientSummaryDto.getHomeShowInfo();
                    Map<String, RobotCustomInfoDto> map = homeShowInfo.stream().collect(Collectors.toMap(RobotCustomInfoDto::getKey, v -> v));
                    answer.setAnswerContent("当前空气质量状态如下：" + status.formatted(
                            map.get("repC3h10Si").getValue(), map.get("repH2").getValue(), map.get("repC3h6").getValue(),
                            map.get("repTemp").getValue(), map.get("repHumidity").getValue()
                    ));
                }
                answer.setMediaStatus("0");
                cvdQuestionAnswerService.updateById(answer);
            }
        }
        while ("0".equals(answer.getMediaStatus())) {
            answer = cvdQuestionAnswerService.getById(answer.getId());
            TimeUnit.SECONDS.sleep(3);
        }
        //生成回答
        String answerContent = answer.getAnswerContent();
        String mediaUrl = answer.getMediaUrl();
        CvdTalkHistoryEntity answerHistory = new CvdTalkHistoryEntity();
        answerHistory.setBelong("system");
        answerHistory.setContent(answerContent);
        String mediaName = mediaUrl.replaceAll("/sys-file/answer_voice/", "");
        String wavPath = fileProperties.getLocal().getBasePath() + "answer_voice" + File.separator + mediaName;
        wavPath = this.normalizePath(wavPath);
        answerHistory.setMediaUrl(wavPath);
        answerHistory.setIsPlay("0");//未播放
        answerHistory.setIsConvert("1");//已转换
        cvdTalkHistoryService.save(answerHistory);
    }


    /**
     * 语音转文字
     *
     * @param wavPath
     * @return
     */
    private String voiceToText(String wavPath) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.valueOf("application/json;UTF-8"));
        JSONObject jsonObject = new JSONObject();
        String webUrl = "http://" + serverRealIp + ":" + serverPort + serverServletContextPath + wavPath;
        jsonObject.put("path", webUrl);
        HttpEntity<String> strEntity = new HttpEntity<String>(jsonObject.toString(), headers);
        log.info("语音转文字接口，\nurl：{}\n入参：{}", webUrl, jsonObject);
        JSONObject result = restTemplate.postForObject(voiceToTextUrl, strEntity, JSONObject.class);
        log.info("语音转文字接口，结果：{}", result);
        String text = result.getString("result");
        return text;
    }

    /**
     * 音频录制
     *
     * @param operate 操作：0-开始录制、1-停止录制
     * @return
     */
    private String recordVoice(int operate) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.valueOf("application/json;UTF-8"));
        String body = JSONUtil.toJsonStr(Map.of("operate", operate, "deviceNo", 3994));
        HttpEntity<String> strEntity = new HttpEntity<String>(body, headers);
        log.info("音频录制接口，入参：{}", body);
        String result = restTemplate.postForObject(AUDIO_PLAY_RECORD, strEntity, String.class);
        log.info("音频录制接口，结果：{}", result);
        JSONObject jsonObject = JSONObject.parseObject(result);
        int code = jsonObject.getInteger("code");
        String msg = jsonObject.getString("msg");
        if (code != 0) {
            throw new RuntimeException("音频录制接口异常:" + msg);
        } else {
            return jsonObject.getString("data");
        }
    }

    /**
     * 执行迎宾讲解任务步骤
     */
    @Scheduled(fixedDelay = 1000L)
    public void executeTaskStep() {
        try {
            RobotEntity robot = robotService.getOne(Wrappers.<RobotEntity>lambdaQuery().last("limit 1"));
            if (robot == null) {
                throw new RuntimeException("未找到机器人！");
            }
            CvdGuideTaskEntity task = cvdGuideTaskService.getOne(Wrappers.<CvdGuideTaskEntity>lambdaQuery().last("limit 1"));
            if (task == null) {
                throw new RuntimeException("未找到迎宾讲解任务！");
            }
            GuideTaskStatusEnum taskStatus = GuideTaskStatusEnum.getByCode(task.getStatus());
            if (taskStatus == GuideTaskStatusEnum.RUNNING) {
                Long execNo = task.getExecNo();
                CvdGuideTaskStepEntity stepTask = cvdGuideTaskStepService.getOne(Wrappers.<CvdGuideTaskStepEntity>lambdaQuery()
                        .eq(CvdGuideTaskStepEntity::getExecNo, execNo)
                        .eq(CvdGuideTaskStepEntity::getStatus, GuideTaskStepStatusEnum.INIT.getCode())
                        .orderByAsc(CvdGuideTaskStepEntity::getId)
                        .last("limit 1"));
                if (stepTask != null) {
                    GuideTaskStepActionEnum action = GuideTaskStepActionEnum.getByCode(stepTask.getAction());
                    switch (action) {
                        case GO:
                        case END: {
                            Map<String, String> param = JSONUtil.toBean(stepTask.getParam(), Map.class);
                            String posCode = param.get("code");
                            //导航前往点位
                            CmdRobot2PosDto dto = new CmdRobot2PosDto();
                            dto.setClientId(robot.getRobotSn());
                            dto.setPosCode(posCode);
                            SpringUtil.getApplicationContext().publishEvent(dto);
                            //状态改成进行中
                            stepTask.setStatus(GuideTaskStepStatusEnum.DOING.getCode());
                            cvdGuideTaskStepService.updateById(stepTask);
                            TimeUnit.SECONDS.sleep(2);
                            //等待到达点位
                            Cache<String, RobotClientSummaryDto> posInfoDtoCache = SpringUtil.getBean("robotCache");
                            RobotClientSummaryDto summaryDto = posInfoDtoCache.getIfPresent(robot.getRobotSn());
                            while (summaryDto == null) {
                                posInfoDtoCache = SpringUtil.getBean("robotCache");
                                summaryDto = posInfoDtoCache.getIfPresent(robot.getRobotSn());
                                TimeUnit.SECONDS.sleep(1);
                            }
                            RobotLaserPosDto laserPos = summaryDto.getPosition().getLaserPos();
                            while (!(laserPos.getIsReached() && posCode.equals(laserPos.getNearPosCode()))) {
                                stepTask = cvdGuideTaskStepService.getById(stepTask.getId());
                                if (GuideTaskStepStatusEnum.getByCode(stepTask.getStatus()) != GuideTaskStepStatusEnum.DOING) {
                                    return;
                                }
                                posInfoDtoCache = SpringUtil.getBean("robotCache");
                                summaryDto = posInfoDtoCache.getIfPresent(robot.getRobotSn());
                                while (summaryDto == null) {
                                    posInfoDtoCache = SpringUtil.getBean("robotCache");
                                    summaryDto = posInfoDtoCache.getIfPresent(robot.getRobotSn());
                                    TimeUnit.SECONDS.sleep(1);
                                }
                                laserPos = summaryDto.getPosition().getLaserPos();
//                                int navTaskStatus = summaryDto.getStatus().getTaskStatus();
//                                if (!posCode.equals(laserPos.getNearPosCode())
//                                        && (navTaskStatus == 0 || navTaskStatus == 4)) {//0-无前往导航点的任务 1-等待 2-正在前往导航点 3-暂停 4-完成 5-失败 6-退出 7-等待开/关门
//                                    //重新下发前往点位
//                                    log.warn("当前导航状态值：{}，重新下发前目标点：{}", navTaskStatus, posCode);
//                                    SpringUtil.getApplicationContext().publishEvent(dto);
//                                }
//                                TimeUnit.SECONDS.sleep(1);
                            }
                            break;
                        }
                        case SPEAK: {
                            //播放语音
                            Map<String, String> param = JSONUtil.toBean(stepTask.getParam(), Map.class);
                            String voiceId = param.get("voiceId");
                            CvdVoiceEntity voice = cvdVoiceService.getById(voiceId);
                            if (voice != null) {
                                String mediaUrl = voice.getMediaUrl();
                                String mediaName = mediaUrl.replaceAll("/sys-file/guide_voice/", "");
                                String basePath = fileProperties.getLocal().getBasePath();
                                this.voiceOperate(1, "");
                                this.voiceOperate(0, basePath + "guide_voice" + File.separator + mediaName);
                                //状态改成进行中
                                stepTask.setStatus(GuideTaskStepStatusEnum.DOING.getCode());
                                cvdGuideTaskStepService.updateById(stepTask);
                                TimeUnit.SECONDS.sleep(2);
                                //等待播放完成
                                while (!this.voiceIsFinished()) {
                                    TimeUnit.SECONDS.sleep(1);
                                }
                            }
                            break;
                        }
                    }
                    task = cvdGuideTaskService.getOne(Wrappers.<CvdGuideTaskEntity>lambdaQuery().last("limit 1"));
                    taskStatus = GuideTaskStatusEnum.getByCode(task.getStatus());
                    if (taskStatus == GuideTaskStatusEnum.RUNNING) {
                        stepTask.setStatus(GuideTaskStepStatusEnum.DONE.getCode());
                        cvdGuideTaskStepService.updateById(stepTask);
                    }
                } else {//完成迎宾讲解
                    task.setStatus(GuideTaskStatusEnum.FREE.getCode());
                    cvdGuideTaskService.updateById(task);
                }
            }
        } catch (Exception e) {
            log.error("执行迎宾讲解任务步骤失败:{}", e.getMessage(), e);
        }
    }


    /**
     * 推送迎宾讲解任务信息
     */
    @Scheduled(fixedDelay = 1000L)
    public void sendGuideTask() {
        try {
            CvdGuideTaskEntity task = cvdGuideTaskService.getOne(Wrappers.<CvdGuideTaskEntity>lambdaQuery().last("limit 1"));
            String pos = task.getPos();
            List<Long> sortedPosList = Arrays.stream(pos.split(",")).map(Long::parseLong).toList();
            List<CvdGuidePosEntity> allPosList = cvdGuidePosService.list(Wrappers.<CvdGuidePosEntity>lambdaQuery()
                    .in(CvdGuidePosEntity::getId, sortedPosList));
            // 按照 sortedPosList 的顺序对allPosList进行排序
            Map<Long, CvdGuidePosEntity> allPosListMap = allPosList.stream().collect(Collectors.toMap(CvdGuidePosEntity::getId, v -> v));
            List<CvdGuidePosEntity> sortedAllPosList = sortedPosList.stream()
                    .map(allPosListMap::get)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());

            List<CvdGuidePosEntity> sortedDonePosList = new ArrayList<>();
            List<String> sortedSpeakContentList = new ArrayList<>();
            if (task != null && (task.getStatus().equals(GuideTaskStatusEnum.RUNNING.getCode()) || task.getStatus().equals(GuideTaskStatusEnum.PAUSE.getCode()))) {
                List<CvdGuideTaskStepEntity> sortedDoneStepList = cvdGuideTaskStepService.list(Wrappers.<CvdGuideTaskStepEntity>lambdaQuery()
                        .eq(CvdGuideTaskStepEntity::getExecNo, task.getExecNo())
                        .eq(CvdGuideTaskStepEntity::getStatus, GuideTaskStepStatusEnum.DONE.getCode())
                        .orderByAsc(CvdGuideTaskStepEntity::getId));
                if (CollectionUtil.isNotEmpty(sortedDoneStepList)) {
                    List<Long> sortedDonePosIds = sortedDoneStepList.stream().map(CvdGuideTaskStepEntity::getGuidePosId).toList();
                    List<CvdGuidePosEntity> donePosList = cvdGuidePosService.list(Wrappers.<CvdGuidePosEntity>lambdaQuery()
                            .in(CvdGuidePosEntity::getId, sortedDonePosIds));
                    // 按照 sortedDonePosIds 的顺序对donePosList结果进行排序
                    Map<Long, CvdGuidePosEntity> guidePosMap = donePosList.stream()
                            .collect(Collectors.toMap(CvdGuidePosEntity::getId, Function.identity()));
                    sortedDonePosList = sortedDonePosIds.stream()
                            .distinct()
                            .map(guidePosMap::get)
                            .filter(Objects::nonNull)
                            .collect(Collectors.toList());

                    List<Long> sortedDoneVoiceIds = sortedDonePosList.stream().map(CvdGuidePosEntity::getVoiceId).toList();
                    List<CvdVoiceEntity> doneVoiceList = cvdVoiceService.list(Wrappers.<CvdVoiceEntity>lambdaQuery()
                            .in(CvdVoiceEntity::getId, sortedDoneVoiceIds));
                    // 按照 sortedDoneVoiceIds 的顺序对speakContentList结果进行排序
                    Map<Long, CvdVoiceEntity> voiceMap = doneVoiceList.stream()
                            .collect(Collectors.toMap(CvdVoiceEntity::getId, Function.identity()));
                    List<CvdVoiceEntity> sortedDoneVoiceList = sortedDoneVoiceIds.stream()
                            .map(voiceMap::get)
                            .filter(Objects::nonNull)
                            .collect(Collectors.toList());
                    sortedSpeakContentList = sortedDoneVoiceList.stream().map(CvdVoiceEntity::getContent).toList();
                }
            }

            CvdGuideTaskStatusDTO dto = new CvdGuideTaskStatusDTO();
            dto.setStatus(task.getStatus());
            dto.setAllPosList(sortedAllPosList);
            dto.setDonePosList(sortedDonePosList);
            dto.setSpeakContents(sortedSpeakContentList);
            // 广播消息给所有订阅的会话
            SubscriptionEndpoint.broadcast(TOPIC_GUIDE, JSONUtil.toJsonStr(dto));
        } catch (Exception e) {
            log.error("推送迎宾讲解任务信息失败:{}", e.getMessage(), e);
        }
    }


    /**
     * 执行语音交互播放
     */
    @Scheduled(fixedDelay = 1000L)
    public void playAnswerVoice() {
        try {
            CvdTalkHistoryEntity answerHistory = cvdTalkHistoryService.getOne(Wrappers.<CvdTalkHistoryEntity>lambdaQuery()
                    .eq(CvdTalkHistoryEntity::getIsPlay, "0")
                    .eq(CvdTalkHistoryEntity::getIsConvert, "1")
                    .orderByAsc(CvdTalkHistoryEntity::getId)
                    .last("limit 1")
            );
            if (answerHistory != null) {
                this.voiceOperate(0, answerHistory.getMediaUrl());
                TimeUnit.SECONDS.sleep(2);
                //等待播放完成
                while (!this.voiceIsFinished()) {
                    TimeUnit.SECONDS.sleep(3);
                }
                //更新状态
                answerHistory.setIsPlay("1");
                cvdTalkHistoryService.updateById(answerHistory);
            }
        } catch (Exception e) {
            log.error("执行语音交互播放失败:{}", e.getMessage(), e);
        }
    }

    /**
     * 推送语音交互回答文本
     */
    @Scheduled(fixedDelay = 1000L)
    public void sendQuestionAnswerText() {
        try {
            List<CvdTalkHistoryEntity> list = cvdTalkHistoryService.list(Wrappers.<CvdTalkHistoryEntity>lambdaQuery()
                    .orderByDesc(CvdTalkHistoryEntity::getId)
                    .last("limit 15")
            );
            if (CollectionUtil.isNotEmpty(list)) {
                // 排序
                list = list.stream().sorted(Comparator.comparing(CvdTalkHistoryEntity::getCreateTime)).toList();
                // 广播消息给所有订阅的会话
                SubscriptionEndpoint.broadcast(TOPIC_QUESTION, JSONUtil.toJsonStr(list));
            }
        } catch (Exception e) {
            log.error("推送语音交互问答文本失败:{}", e.getMessage(), e);
        }
    }

    private String normalizePath(String path) {
        return path.replace("\\", "/");
    }
}